home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / zcpp_jae.zip / MACRO.C < prev    next >
C/C++ Source or Header  |  1990-07-06  |  13KB  |  490 lines

  1. /*
  2.  
  3.  
  4.  Copyright (C) 1990 Texas Instruments Incorporated.
  5.  
  6.  Permission is granted to any individual or institution to use, copy, modify,
  7.  and distribute this software, provided that this complete copyright and
  8.  permission notice is maintained, intact, in all copies and supporting
  9.  documentation.
  10.  
  11.  Texas Instruments Incorporated provides this software "as is" without
  12.  express or implied warranty.
  13.  
  14.  
  15.  *
  16.  * Edit history
  17.  * Created: LGO 30-Mar-89 -- Initial design and implementation.
  18.  *
  19.  * MACRO defmacro
  20.  *
  21.  * CPP defmacro for building macros which can include cpp directives.
  22.  * MACRO works like #define, except the macro body is NOT limited to a
  23.  * single line, and may contain arbitrary cpp directives.  Optional,
  24.  * keyword and body parameters are accepted.
  25.  *
  26.  * To define a macro, the following must have been encountered in the input
  27.  * stream: 
  28.  *     #pragma defmacro MACRO "macro" delimiter=}
  29.  *
  30.  * A macro definition has the form:
  31.  *    MACRO name(arglist) { body }
  32.  *
  33.  * where name is the name of the macro, arglist are argument
  34.  * specifiers seperated by comma's and body is what is substituted on a 
  35.  * call to the macro. arglist may specify positional, optional,
  36.  * optional keyword, required keyword, rest and body arguments.
  37.  * The syntax is:
  38.  * arglist := [KEY: | REST: | BODY] identifier [= identifier] [, arglist]
  39.  *
  40.  * The equals sign indicates a default argument value.
  41.  * When KEY: is encountered, the rest of the arguments are all keyword
  42.  * arguments. A rest parameter will be set to all remaining parameter
  43.  * values during macro expansion.  REST: parm=name indicates that "parm"
  44.  * will hold the rest of the parameters, and "name" will hold the number
  45.  * of parameters in "parm".  BODY: parm indicates that "parm" will expand to
  46.  * the text within braces {} after the macro call. The braces are removed.
  47.  *
  48.  * Example 1:
  49.  *    MACRO set_val (size, value=NULL, KEY: low = 0, high = 100)
  50.  *    { set_val_internal(size, val, low, high-low) }
  51.  *
  52.  *    set_val    is the macro name
  53.  *    size    is a required positional parameter
  54.  *    val    is an optional positional parameter
  55.  *    low    is an optional keyword parameter
  56.  *    high    is a required keyword parameter
  57.  *
  58.  * Example 2:
  59.  *   // Build a table of char*'s
  60.  *   MACRO build_table(name, REST: rest)
  61.  *   { char* name[] = { build_table_internal(rest) NULL} }
  62.  *
  63.  *   MACRO build_table_internal(first, REST: rest=count)
  64.  *   {#first,
  65.  *   #if count // recurse on rest args
  66.  *   build_table_internal(rest)
  67.  *   #endif
  68.  *   }
  69.  *
  70.  *   build_table(table, 1,2,3,4,5,6,7);   // expands to
  71.  *   char* table[] = {"1", "2", "3", "4", "5", "6", "7"};
  72.  * 
  73.  * Example 3:
  74.  *
  75.  *   MACRO LOOP (type, variable, sequence, BODY: body) {
  76.  *   { type varible;
  77.  *     for(sequence.reset; sequence.next;) {
  78.  *       variable = sequence.value();
  79.  *       body
  80.  *       }
  81.  *   }}
  82.  *
  83.  *   LOOP(char, elt, list) { print(elt) } // Expands to:
  84.  *
  85.  *   { char varible;
  86.  *     for(list.reset; list.next;) {
  87.  *       elt = list.value();
  88.  *       print(elt)
  89.  *     }
  90.  *   }
  91.  *
  92.  * Error messages: (first %s is macro name)
  93.  *    %s: Can't find argument list
  94.  *    %s: REST and BODY args must be last
  95.  *    %s: Unknown parameter keyword: %s:
  96.  *    %s: Syntax error, '%c' found after %s=%s
  97.  *    %s: Syntax error, '%c' found after %s
  98.  *    %s: The required argument %s is missing
  99.  *    %s: Keyword missing for %s
  100.  *    %s: The required keyword argument %s is missing
  101.  *    %s: body argument not found
  102.  *    %s: Unknown argument: %s
  103.  *    %s: Missing ; after macro body
  104.  */
  105.  
  106. #include "defmacio.h"
  107. #include "macro.h"
  108.  
  109. typedef struct mac_def {
  110.   char* name;
  111.   Arg* args;
  112.   char* body;
  113. } Mac_Def;
  114.  
  115. #define BSIZE 512
  116. #define MAXPARM 32
  117.  
  118. Arg arg_error;                  /* Global error flag */
  119. /*
  120.  * Parse macro args
  121.  */
  122. Arg*
  123. macro_args(macname)
  124.    char* macname;
  125. {
  126.   char c;
  127.   char* name;
  128.   char* value;
  129.   Type type = p_positional;
  130.   Arg* result = NULL;
  131.   Arg* next = NULL;
  132.   Arg* new;
  133.   Boolean rest_or_body = FALSE;
  134.  
  135.   c = skip_blanks();
  136.   if(c != '(') {
  137.     fprintf(stderr, "%s: Can't find argument list", macname);
  138.     return &arg_error;
  139.   }
  140.   do {
  141.     if(type==p_body || type==p_rest)
  142.       rest_or_body = TRUE;
  143.     else if (rest_or_body) {
  144.       fprintf(stderr, "%s: REST and BODY args must be last", macname);
  145.       return &arg_error;
  146.     }
  147.     value="";
  148.     name=scan_token(",):=");
  149.     c = getchar();
  150.     if (c==':') {
  151.       if(!strcmp(name, "KEY"))
  152.     type=p_key;
  153.       else if (!strcmp(name, "REST"))
  154.     type=p_rest;
  155.       else if (!strcmp(name, "BODY"))
  156.     type=p_body;
  157.       else {
  158.     fprintf(stderr, "%s: Unknown parameter keyword: %s", macname, name);
  159.     return &arg_error;
  160.       }
  161.       name=scan_token(",):=");
  162.       c = getchar();
  163.     }
  164.     switch (c) {
  165.     case ')': break;
  166.     case ',': break;
  167.     case '=':
  168.       value=scan_token(",)");
  169.       c=getchar();
  170.       if(c == ',' || c == ')') break;
  171.       fprintf(stderr, "%s: Syntax error, '%c' found after %s=%s",
  172.           macname, c, name, value);
  173.       return &arg_error;
  174.     default:
  175.       fprintf(stderr, "%s: Syntax error, '%c' found after %s", macname, c, name);
  176.       return &arg_error;
  177.     }
  178.     new = (Arg*) getmem(sizeof(Arg));      /* Create new Arg structure */
  179.     if (result == NULL)
  180.       result = new;
  181.     else 
  182.       next->next = new;
  183.     next = new;
  184.     new->next = NULL;
  185.     new->name = name;
  186.     new->value = value;
  187.     new->type = type;      
  188.   } while (c != ')');
  189.   return result;
  190. }
  191. /*
  192.  * Write body returning when a token is found
  193.  */
  194. static char*
  195. get_token(body, tokenbuf, modifier)
  196.   char* body;
  197.   char* tokenbuf;
  198.   Modifier* modifier;
  199. {
  200.   char c;
  201.   char p = EOS;
  202.   char* bodyp = body;
  203.   char* tokenp = tokenbuf;
  204.   *modifier = mod_none;
  205.   while((c = *bodyp++) != EOS) {
  206.     if(isalnum(c) || c=='_' || c=='$') {
  207.       *tokenp++ = c;
  208.       while((c = *bodyp++) != EOS) {
  209.     if((isalnum(c) || c=='_' || c=='$'))
  210.       *tokenp++ = c;
  211.     else {                  /* End of token */
  212.       *tokenp = EOS;
  213.       return bodyp-1;
  214.     }
  215.       }                      /* End of body with token */
  216.       *tokenp = EOS;
  217.       return bodyp-1;
  218.     }
  219.     else if(c == '#') {
  220.       if (*bodyp == '#') {          /* token catenation */
  221.     *modifier = mod_concat;
  222.     bodyp++;
  223.     while(isspace(*bodyp)) bodyp++;
  224.       } else {
  225.     if (p != EOS) putchar(p);
  226.     if (isalnum(*bodyp)) {          /* token quoting */
  227.       *modifier = mod_quote;
  228.     } else if(*bodyp == '~') {      /* Special hack #~ for unquote */
  229.       bodyp++;
  230.       *modifier = mod_unquote;
  231.     } else {
  232.       putchar(c);
  233.     }
  234.       }
  235.     } else {
  236.       if (*bodyp == '#')
  237.     p = c;
  238.       else {
  239.     putchar(c);
  240.     p = EOS;
  241.       }
  242.     }
  243.   }
  244.   *tokenp = EOS;
  245.   return bodyp-1;              /* End of body no token */
  246. }
  247. /* 
  248.  * Write body substituting args for values
  249.  */
  250. void
  251. macro_substitute(body, nparm, parnames, parvalues)
  252.   char* body;
  253.   int nparm;
  254.   char** parnames;
  255.   char** parvalues;
  256.   Modifier modifier;
  257.   char tokenbuf[BSIZE];
  258.   int i;
  259.   Boolean prev_match = FALSE;
  260.   do {
  261.     body = get_token(body, tokenbuf, &modifier);
  262.     for(i=nparm; i--;) {
  263.       if(!strcmp(tokenbuf, parnames[i])) {
  264.     char* val = parvalues[i];
  265.     prev_match = TRUE;
  266.     switch (modifier) {
  267.     case mod_quote:
  268.       put_string(val);
  269.       break;
  270.     case mod_unquote:
  271.       { char c;
  272.         val++;
  273.         while((c=*val++) != EOS)
  274.           if(*val == EOS) break;
  275.           else if(!(c == '\\' && *val == '"'))
  276.         putchar(c);
  277.       }
  278.       break;
  279.     default:
  280.       puts(val);
  281.       break;
  282.     }
  283.     goto next;
  284.       }
  285.     }
  286.     switch (modifier) {
  287.     case mod_concat:
  288.       if (prev_match) break;
  289.       putchar('#');
  290.     case mod_quote: putchar('#'); break;
  291.     case mod_unquote: puts("#~"); break;
  292.     }
  293.     puts(tokenbuf);
  294.     prev_match = FALSE;
  295.   next:
  296.     ;
  297.   }
  298.   while (*body != EOS);
  299. }
  300. /*
  301.  * Expand a macro defined by define_macro
  302.  */
  303. expand_macro(argc, argv)
  304.      int argc;
  305.      char* argv[];
  306. {
  307.   Mac_Def* mac = (Mac_Def*) argv[1];
  308.   char c;
  309.   Arg* call_argp;
  310.   Arg* def_argp = mac->args;
  311.   Arg* args = NULL;
  312.   Arg* argp;
  313.   char* bodyarg = NULL;
  314.   int nparm = 0;
  315.   char* parnames[MAXPARM];
  316.   char* parvalues[MAXPARM];
  317.   char junk[BSIZE];
  318.   int n;
  319.   char nstr[4];
  320.  
  321.   if(copytoken(junk) == NULL)      /* Skip macro name */
  322.     return(1);
  323.     
  324.   if(def_argp && def_argp->type != p_body) {
  325.     if((args = macro_args(mac->name)) == &arg_error) {  /* Gather arguments */
  326.       fprintf(stderr, " in %s\n", junk);
  327.       return(1);
  328.     }
  329.   }
  330.   call_argp = args;              /* Match calling and defining args */
  331.   for (; def_argp != NULL; def_argp = def_argp->next) {
  332.     switch (def_argp->type) {
  333.     case p_positional:
  334.       parnames[nparm] = def_argp->name;
  335.       if(call_argp != NULL) {
  336.     parvalues[nparm] = (*call_argp->value != EOS) ?
  337.       call_argp->value : call_argp->name;
  338.     call_argp = call_argp ->next;
  339.       } else if (*def_argp->value != EOS)
  340.     parvalues[nparm] = def_argp->value;
  341.       else {
  342.     fprintf(stderr, "%s: The required argument %s is missing\n",
  343.         mac->name, def_argp->name);
  344.     return 1;
  345.       }
  346.       break;
  347.     case p_rest:
  348.       parnames[nparm] = def_argp->name;
  349.       *junk = EOS;
  350.       n = 0;
  351.       for(;call_argp != NULL; call_argp = call_argp->next) {
  352.     strcat(junk, call_argp->name);
  353.     if (*call_argp->value != EOS) {
  354.       strcat(junk, "=");
  355.       strcat(junk, call_argp->value);
  356.     }
  357.     if (call_argp->next)
  358.       strcat(junk, ", ");
  359.     n++;
  360.       }
  361.       parvalues[nparm] = junk;
  362.       /* If rest arg has a default value */
  363.       if (*def_argp->value != EOS) {      /* Use it as the name of a parameter */
  364.     sprintf(nstr, "%d", n);          /* which holds the number of rest args */
  365.     nparm++;
  366.     parnames[nparm] = def_argp->value;
  367.     parvalues[nparm] = nstr;
  368.       }
  369.       break;
  370.     case p_key:
  371.       parnames[nparm] = def_argp->name;
  372.       parvalues[nparm] = def_argp->value;
  373.       { Arg* argp = call_argp;
  374.     for(;argp != NULL; argp = argp->next) {
  375.       if(*argp->name == EOS) {
  376.         fprintf(stderr, "%s: Keyword missing for %s\n",
  377.             mac->name, argp->value);
  378.         return 1;
  379.       }
  380.       if(!strcmp(argp->name, def_argp->name)) {
  381.         parvalues[nparm] = argp->value;
  382.         argp->type = p_key;          /* Munge type to indicate used */
  383.         break;
  384.       }
  385.     }
  386.       }
  387.       if (*parvalues[nparm] == EOS) {
  388.     fprintf(stderr, "%s: The required keyword argument %s is missing\n",
  389.         mac->name, def_argp->name);
  390.     return 1;
  391.       }
  392.       break;
  393.     case p_body:
  394.       c = skip_blanks();
  395.       if (c != '{') {
  396.     fprintf(stderr, "%s: body argument not found\n", mac->name);
  397.     do { putchar(c); }          /* error recovery */
  398.     while ((c=getchar()) != EOF);
  399.     return 1;
  400.       }
  401.       unget();
  402.       bodyarg = scan_next('}');          /* Grab the macro body */
  403.       {char* bp = bodyarg + strlen(bodyarg) - 1;
  404.        *bp = EOS;}              /* Strip {} from body */
  405.       parnames[nparm] = def_argp->name;
  406.       parvalues[nparm] = bodyarg+1;
  407.       break;
  408.     }
  409.     nparm++;
  410.   }
  411.   if (call_argp != NULL) {          /* Check for unused keyargs */
  412.     for(argp = call_argp; argp != NULL; argp = argp->next)
  413.       if (argp->type != p_key) {
  414.     fprintf(stderr, "%s: Unknown argument: %s\n",
  415.         mac->name, argp->name);
  416.     return 1;
  417.       }
  418.   } 
  419.                       /* Substitute args for values */
  420.   macro_substitute(mac->body, nparm, parnames, parvalues);
  421.   for(argp = args; argp != NULL;) {
  422.     Arg* old = argp;
  423.     argp = argp->next;
  424.     free(old->name);
  425.     if (*old->value) free(old->value);
  426.     free(old);
  427.   }
  428.   return 0;
  429. }
  430. /*
  431.  * Define a new macro
  432.  */
  433. int
  434. define_macro(argc, argv)
  435.      int argc;
  436.      char* argv[];
  437. {
  438.   char* name;
  439.   Arg* args;
  440.   char* body;
  441.   Mac_Def* mac;
  442.   char expanding = 0;
  443.  
  444.   { char macname[BSIZE];
  445.     if(copytoken(macname) == NULL)      /* Skip macro name */
  446.       return(1);
  447.     if(copytoken(macname) == NULL)      /* Copy the macro name */
  448.       return(1);
  449.     if(!strcmp(macname, "EXPANDING")) {
  450.       expanding = 1;
  451.       if(copytoken(macname) == NULL)      /* Copy the macro name */
  452.     return(1);
  453.     }
  454.     name = savestring(macname);
  455.   }
  456.   if((args = macro_args("MACRO")) == &arg_error) {  /* Gather arguments */
  457.     fprintf(stderr, " in %s\n", name);
  458.     return(1);
  459.   }
  460.   skip_blanks();
  461.   unget();
  462.   body = savestring(scan_next('}'));          /* Grab the macro body */
  463.   { char* bp;
  464.     while(*body++ != '{');          /* strip start of body */
  465.     bp = body + strlen(body);          /* Strip end of body */
  466.     *--bp = EOS;
  467.   }
  468.   mac = (Mac_Def*) getmem(sizeof(Mac_Def));  /* Create new macro structure */
  469.   mac->name = name;
  470.   mac->args = args;
  471.   mac->body = body;
  472.   { char delim = ')';
  473.     Boolean recursive = FALSE;
  474.     char* arglist[3];
  475.     arglist[0] = name;
  476.     arglist[1] = (char*) mac;
  477.     arglist[2] = NULL;
  478.     if (args != NULL) {
  479.       Arg* a = args;
  480.       while(a->next!=NULL) a = a->next;      /* Find last parameter */
  481.       if(a->type == p_body) delim = '}';
  482.       if(a->type == p_rest || a->type == p_body) recursive = TRUE;
  483.     }
  484.     new_defmacro(name, expanding, recursive, delim, EOS,
  485.          expand_macro, name, arglist);
  486.   }
  487.   return 0;
  488. }
  489.